<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script>
    function isFunc(fun) {
      return typeof fun === 'function'
    }
    function isObj(obj) {
      return typeof obj === 'object'
    }
    function resolvePromise(promise, data, resolve, reject) {
      if (promise === data) {
        return reject(new TypeError("禁止循环引用"))
      }
      let called = false
      if (isObj(data) && data !== null || isFunc(data)) {
        try {
          const then = data.then
          if (isFunc(then)) {
            then.call(data, value => {
              if (called) return
              called = true
              resolvePromise(data, value, resolve, reject)
            }, reason => {
              if (called) return
              called = true
              reject(reason)
            })
          } else {
            resolve(data)
          }
        } catch (e) {
          if (called) return
          called = true
          reject(e)
        }
      } else {
        resolve(data)
      }
    }
    const PENDING = 'pending'
    const REJECTED = 'rejected'
    const FULFILLED = 'fulfilled'
    class MyPromise {
      constructor(executor) {
        const that = this
        this.status = PENDING
        this.onFulfilledCallbacks = []
        this.onRejectedCallbacks = []
        if (typeof executor !== 'function') throw TypeError('executor must be a function')
        function resolve(value) {
          if (that.status === PENDING) {
            that.status = FULFILLED
            that.value = value
            that.onFulfilledCallbacks.forEach(cb => {
              cb(value)
            })
          }
        }
        function reject(reason) {
          if (that.status === PENDING) {
            that.status = REJECTED
            that.reason = reason
            that.onRejectedCallbacks.forEach(cb => {
              cb(reason)
            })
          }
        }
        try {
          executor.call(that, resolve, reject)
        } catch (e) {
          reject(e)
        }
      }
      then(onFulfilled, onRejected) {
        const that = this
        onFulfilled = isFunc(onFulfilled) ? onFulfilled : (value) => value
        onRejected = isFunc(onRejected) ? onRejected : (reason) => {
          throw reason
        }
        const mypromise = new MyPromise((resolve, reject) => {
          function fulfilledCallback(value) {
            queueMicrotask(() => {
              try {
                const result = onFulfilled(value)
                resolvePromise(promise, result, resolve, reject)
              } catch (e) {
                reject(e)
              }
            })
          }
          function rejectedCallback(reason) {
            queueMicrotask(() => {
              try {
                const result = onRejected(reason)
                resolvePromise(promise, result, resolve, reject)
              } catch (e) {
                reject(e)
              }
            })
          }
          switch (that.status) {
            case FULFILLED:
              fulfilledCallback(that.value)
              break
            case REJECTED:
              rejectedCallback(that.reason)
              break
            default:
              {
                that.onFulfilledCallbacks.push(onFulfilled)
                that.onRejectedCallbacks.push(onRejected)
                break
              }
          }
        })
        return mypromise
      }
      catch(onRejected) {
        return this.then(null, onRejected)
      }
      finally(onFinally) {
        return this.then(value => MyPromise.resolve(onFinally()).then(() => value,
          newReason => { throw newReason }
        ), (reason) => MyPromise.resolve(onFinally()).then(() => {
          throw reason
        }, newReason => {
          throw newReason
        }))
      }
      static resolve(value) {
        if (value instanceof MyPromise) return value
        return new MyPromise((resolve) => {
          resolve(value)
        })
      }
      static reject(reason) {
        return new Promise((resolve, reject) => {
          reject(reason)
        })
      }
    }
    let p = new MyPromise((resolve, reject) => {
      setTimeout(resolve, 3000)
    }).then(res => {
      console.log(res);
      console.log('===');
    })
  </script>
</body>

</html>